/*
 * Copyright 2011 Marcin Blazejewski
 *
 * This file is part of Ground0
 *
 * Ground0 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Ground0 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Ground0. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "properties.hpp"

#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>

using namespace std;

static string trim(string s) {
    int from, to;
    from = s.find_first_not_of(" \t");
    to = s.find_last_not_of(" \t");
    
    return s.substr(from, to + 1);
}

string lower(string &s) {
    string out;

    for (int c = 0; c < s.size(); c++) {
        out += tolower(s[c]);
    }

    return out;
}

/** Contruct empty properties 
  */
Properties::Properties() {
}

/** Load properties from a file.
 * @see read_file for file format
 */
Properties::Properties(const char *file) {
    read_file(file);
}

/** Load properties from a file.
 * New properties are appended to current set.
 * File format:<br>
 * # comment <br>
 * <key> = <value><br>
 * Blank lines and comments are ignored.<br>
 * Leading and trailing whitespaces are discarded from both key and value.
 */
void Properties::read_file(const char *filename) {
    string line;
    string key;
    string value;
    fstream cfg_file(filename, fstream::in);
    
    if (!cfg_file.is_open()) throw string("cannot open property file: ") + filename;
    
    while (getline(cfg_file, line)) {
        int eqPos = line.find('=');
        
        if (eqPos != -1) {
            line = trim(line);
            if (line[0] == '#') continue;
            
            key = trim(line.substr(0, eqPos));
            value = trim(line.substr(eqPos + 1, -1));
            
            data[key] = value;
        }
    }
    
    cfg_file.close();

}

/** Get value as string, or defval if key not found.
 */
string Properties::get_str(const char *key, const char *defval) {
    map<string, string>::iterator i = data.find(key);
    if (i == data.end()) {
        if (defval) {
            return string(defval);
        }
        throw string("no such key: ") + key;
    }

    return i->second;
}

/** Get value as string. Throws exception if key not found.
 */
const string& Properties::get_str(const char *key) {
    map<string, string>::iterator i = data.find(key);
    if (i == data.end()) throw string("no such key: ") + key;

    return i->second;
}

/** Get value as int, or defval if key not found.
 */
int Properties::get_int(const char *key, const int defval) {
    map<string, string>::iterator i = data.find(key);
    if (i == data.end()) {
        return defval;
    }

    return atoi(i->second.c_str());
}

/** Get value as int. Throws exception if key not found.
 */
int Properties::get_int(const char *key) {
        map<string, string>::iterator i = data.find(key);
    if (i == data.end()) throw string("no such key: ") + key;

    return atoi(i->second.c_str());
}

/** Get value as float, or defval if key not found.
 */
float Properties::get_float(const char *key, const float defval) {
    map<string, string>::iterator i = data.find(key);
    if (i == data.end()) {
        return defval;
    }

    return atof(i->second.c_str());
}

/** Get value as float. Throws exception if key not found.
 */
float Properties::get_float(const char *key) {
    map<string, string>::iterator i = data.find(key);
    if (i == data.end()) throw string("no such key: ") + key;

    return atof(i->second.c_str());
}

/** Set key/value
 */
const string& Properties::set(const char *key, const char *value) {
    data[key] = string(value);
    return data[key];
}



/******************************************************************************/
/* Property cursor */
/******************************************************************************/

PropertyCursor::PropertyCursor(Properties &_src, const char *_prefix) 
    : src(_src.get_data()), prefix(_prefix) {

    prefix_len = strlen(prefix);
    reset();
}

/** Reset cursor to start iterating from the beginning.
 */
void PropertyCursor::reset() {
    i = src.begin();
}

/** Skip to next matching property.
 * @returns true if a matching property can be retireved with get_next.
 */
bool PropertyCursor::has_next() {
    while (i != src.end()) {
        if (strncmp(i->first.c_str(), prefix, prefix_len) == 0) {
            return true;
        }

        i++;
    }

    return false;
}

/** Get property value at current position.
 * @returns property value
 * @throws "no more matches" if called with no matching property
 * at current position (has_next() returns false)
 */
const string& PropertyCursor::get_next() {
    if (i != src.end()) {
        return (i++)->second;
    }
    throw string("no more matches");
}
